package com.hainabo.mgcmall.api;

import com.github.kristofa.brave.http.HttpServerRequest;
import com.hainabo.bootside.core.common.ReturnVO;
import com.hainabo.mgcmall.api.datafetcher.ProductAllDataFetcher;
import com.hainabo.mgcmall.api.datafetcher.PruductIdDataFetcher;
import com.hainabo.mgcmall.dao.Product;
import com.hainabo.mgcmall.dao.ProductRepository;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import java.io.File;
import java.io.IOException;

import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring;

@Api(value = "/product")
@Component
@Path("/product")
@Produces(MediaType.APPLICATION_JSON)
@Slf4j
public class ProductResource {

    @Autowired
    ProductRepository productRepository;

    private GraphQL graphQL;

    @Value("classpath:graphqls/product.graphqls")
    private Resource schemaResource;
    @Autowired
    private ProductAllDataFetcher productAllDataFetcher;
    @Autowired
    private PruductIdDataFetcher pruductIdDataFetcher;

    @PostConstruct
    public void loadSchema() throws IOException {
        log.info("loadSchema  ...  ");
        File schemaFile = schemaResource.getFile();
        SchemaParser schemaParser = new SchemaParser();
        TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schemaFile);
        RuntimeWiring runtimeWiring = buildRuntimeWiring();
        GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
        graphQL = GraphQL.newGraphQL(graphQLSchema).build();
    }

    private RuntimeWiring buildRuntimeWiring() {
        RuntimeWiring runtimeWiring = newRuntimeWiring()
                .type("Query", builder -> builder
                                .dataFetcher("all", productAllDataFetcher)
                .dataFetcher("product", pruductIdDataFetcher)
                ).build();
        return runtimeWiring;
    }

    @POST
    @Path("/query")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = ReturnVO.class)})
    @ApiOperation(value = "graphql query", notes = "## type Query {\n" +
            "\n" +
            "    all: [Product]\n" +
            "\n" +
            "    product(id: Long): Product\n" +
            "\n" +
            "    byStatus(status: String): [Product]\n" +
            "\n" +
            "}" +
            "\n" +
            "## Query:\n" +
            "\n" +
            "    {\n" +
            "        all {\n" +
            "            id,\n" +
            "            name,\n" +
            "            title,\n" +
            "            shotName,\n" +
            "            fullName,\n" +
            "            html,\n" +
            "            status,\n" +
            "            type,\n" +
            "            tags\n" +
            "        }\n" +
            "    }\n" +
            "\n" +
            "## JS API:\n" +
            "       ProductResource.query({$entity:'{all{id,title,name}}'})")
    public ReturnVO query(@Context HttpServletRequest request, String query) {
        log.debug("query product: {}", query);
        try {
            ExecutionResult executionResult = graphQL.execute(query);
            if (executionResult.getErrors().size() > 0) {
                return new ReturnVO(ReturnVO.CODE_FAIL, ReturnVO.MSG_FAIL, executionResult.getErrors());
            }
            Object data = executionResult.getData();
            return new ReturnVO(data);
        } catch (Exception e) {
            e.printStackTrace();
            return new ReturnVO(ReturnVO.CODE_FAIL, ReturnVO.MSG_FAIL, e.getMessage());
        }
    }

    @POST
    @Path("/post")
    public ReturnVO post(Product product) {
        return new ReturnVO(productRepository.save(product));
    }

    @GET
    @Path("/get/{id}")
    public ReturnVO findById(@Context HttpServerRequest request, @PathParam(value = "id") Long id) {
        log.debug("findById: {}", id);
        return new ReturnVO(productRepository.findOne(id));
    }

    @DELETE
    @Path("/delete/{id}")
    public ReturnVO deleteById(@Context HttpServerRequest request, @PathParam(value = "id") Long id) {
        log.debug("deleteById: {}", id);
        if (productRepository.findOne(id) != null) {
            productRepository.delete(id);
            return new ReturnVO("ok");
        } else {
            return new ReturnVO(false, "not found");
        }
    }

    @PUT
    @Path("/put")
    public ReturnVO put(Product product) {
        log.debug("put: {}", product);
        Product old = productRepository.findOne(product.getId());
        if (old == null) {
            return new ReturnVO(ReturnVO.CODE_FAIL, ReturnVO.MSG_FAIL, String.format("entity not exist, id: %s.", product.getId()));
        }
        BeanUtils.copyProperties(product, old);
        return new ReturnVO(productRepository.save(product));
    }

}